#include "Importer.h"

/*-----------------------------------------------------------------------------------------------*/
bool Importer::importSprite(XMLNode& spriteNode)
{
   std::map<std::string, Sprite*>::iterator it;
   
   const char* spriteName = spriteNode.getAttribute("Name");
   Sprite* pSprite = NULL;

   //search for the given sprite on the sprite-map
   for ( it =  m_spriteMap.begin(); it != m_spriteMap.end(); ++it )
   {
      if (it->first.c_str() == spriteName)
      {
         pSprite = it->second;
         return true;
      }
   }

   //if we don't have the sprite on the sprite-map
   pSprite = new Sprite(spriteName);

   m_pCurrentSprite = pSprite;
   
   m_spriteMap[spriteName] = pSprite;

   UINT positionX = atoi( spriteNode.getAttribute("X") );
   UINT positionY = atoi( spriteNode.getAttribute("Y") );
   UINT width  = atoi( spriteNode.getAttribute("width") );
   UINT height = atoi( spriteNode.getAttribute("height") );

   const char* textureXMLFilePath = spriteNode.getAttribute("TextureXMLFile");
   XMLNode mainNode = XMLNode::openFileHelper(textureXMLFilePath);
   XMLNode& textureNode = mainNode.getChildNode("Texture");
   importTexture(textureNode);

   const char* textureFile = textureNode.getAttribute("FilePath");
   
   pSprite->setTexture( m_pTextureMap[textureFile] );

   pSprite->setName(spriteName);
   
   if ( !pSprite->setTextureArea(positionX, positionY, width, height) )
   {
      std::cout << "Failed on Sprite->SetTextureArea for texture" << textureFile << std::endl;
      return false;
   }

   // ANIMATION
   int conut = 0;
   for (int i = 0; i < spriteNode.nChildNode("ANIMATION"); ++i)
   {
      XMLNode& animation = spriteNode.getChildNode("ANIMATION", &conut);
      if ( !importAnimation(animation) )
      {
         std::cout << "Failed on importAnimation" << std::endl;
         return false;
      }
   }

   return true;
}
/*-----------------------------------------------------------------------------------------------*/
bool Importer::importTexture(XMLNode& textureNode)
{
   const char* textureFilePath = textureNode.getAttribute("FilePath");
   Texture::TexturePtr pTexture;

   std::map<std::string, Texture::TexturePtr>::iterator it;

   //look for the texture in the texture map
   for ( it = m_pTextureMap.begin(); it != m_pTextureMap.end(); ++it )
   {
      if( it->first.c_str() == textureFilePath )
      {
         pTexture = it->second;
         return true;
      }
   }

   XMLNode colorKeyNode = textureNode.getChildNode("ColorKey");

   ColorKey colorKey;

   colorKey.red   = atoi( colorKeyNode.getAttribute("R") );
   colorKey.green = atoi( colorKeyNode.getAttribute("G") );
   colorKey.blue  = atoi( colorKeyNode.getAttribute("B") );

   pTexture = Texture::TexturePtr( new Texture(textureFilePath, colorKey) );

   m_pTextureMap[textureFilePath] = pTexture;

   if ( !m_pRenderer->loadTexture(textureFilePath, pTexture) )
   {
      std::cout << "Filed to import texture: " << textureFilePath << std::endl;
   }

   return true;
}
/*-----------------------------------------------------------------------------------------------*/
bool Importer::importResources(const char* xmlTextureFileName)
{
   XMLNode xmlMainNode = XMLNode::openFileHelper(xmlTextureFileName, "SPRITES");

   int cont = 0;
   for(int i=0; i < xmlMainNode.nChildNode("SPRITE"); ++i)
   {
      XMLNode& sprite = xmlMainNode.getChildNode("SPRITE", &cont);
      if ( !importSprite(sprite) )
      {
         std::cout << "Failed to import xmlFile: " << xmlTextureFileName << std::endl;
         return false;
      }
   }

   return true;
}
/*-----------------------------------------------------------------------------------------------*/
bool Importer::createSprite(const char* name, Sprite* sprite)
{
   Sprite* sprite2 = m_spriteMap[name];

   if(!sprite2)
   {
      return false;
   }

   sprite2->clone(sprite);

   return true;
}
/*-----------------------------------------------------------------------------------------------*/
bool Importer::importAnimation(XMLNode& animationNode)
{
   const char* animationName = animationNode.getAttribute("Name");
   
   std::map<std::string, AnimationInfo::AnimationInfoPtr>::iterator it;

   AnimationInfo::AnimationInfoPtr pAnimationInfo;
   
   if ( m_animationMap.count(animationName) )
   {
      return true;
   }

   //if the animation is not inside the map
   pAnimationInfo = AnimationInfo::AnimationInfoPtr( new AnimationInfo() );
   
   const char* time = animationNode.getAttribute("Time"); 
   pAnimationInfo->setLength(atof(time));

   const char* pSpeed = animationNode.getAttribute("Speed");
   pAnimationInfo->setSpeed(atof(pSpeed));

   const std::string pLoop = animationNode.getAttribute("Loop");
   pAnimationInfo->setLoopable(pLoop == "true");

   int conut = 0;
   for(int i = 0; i < animationNode.nChildNode("FRAME"); ++i)
   {
      XMLNode& frame = animationNode.getChildNode("FRAME", &conut);

      unsigned int X = atoi( frame.getAttribute("X") );
      unsigned int Y = atoi( frame.getAttribute("Y") );

      unsigned int width  = atoi( frame.getAttribute("width") );
      unsigned int height = atoi( frame.getAttribute("height") );

      pAnimationInfo->addFrame(X, Y, width, height);
   }

   if ( !m_pCurrentSprite->addAnimationInfo(animationName, pAnimationInfo) )
   {
      std::cout << "addAdnimationInfo failed with " << animationName << std::endl;; 
      return false;
   }

   m_animationMap[animationName] = pAnimationInfo;

   return true;
}
/*-----------------------------------------------------------------------------------------------*/
bool Importer::importModel(const std::string& filePath)
{
   Assimp::Importer importer;

   const aiScene* scene = importer.ReadFile(
      filePath,
      aiPrimitiveType_LINE  |
      aiPrimitiveType_POINT |
      aiProcess_Triangulate |
      aiProcess_SortByPType |
      aiProcess_ConvertToLeftHanded );

   if( !scene )
   {
      std::cout << importer.GetErrorString() << std::endl;
      return false;
   } 

   if (scene->HasMaterials())
   {
      
     
   }

   //we save the path to load the textures
   //NOTE: the textures and the model have to be on the same path
   m_currentModelPath = extractFileLocation(filePath);
   
   std::string filename = extractFileName(filePath);
   
   extractFileLocation(filePath);

   Node* model = new Node(filename);
   
   recursiveLoad(scene, scene->mRootNode, model);

   m_nodeMap[filename] = model;

   return true;  
}
/*-----------------------------------------------------------------------------------------------*/
void Importer::importMeshMaterial( aiMesh* pAiMesh, const aiScene* pAiScene, Mesh* pEngineMesh )
{
   
   aiMaterial* pAiMaterial = pAiScene->mMaterials[pAiMesh->mMaterialIndex];

   //TEXTURE
   for (int i = 0; i < pAiMaterial->GetTextureCount(aiTextureType_DIFFUSE) ; ++i)
   {
      aiString path;
      if ( pAiMaterial->GetTexture(aiTextureType_DIFFUSE, i, &path) != aiReturn_SUCCESS )
      {
         std::cout << "Importer::importMeshMaterial, failed to get texture < " << i << ">" << std::endl; 
         continue;
      }
      std::string filePath = m_currentModelPath + std::string(path.C_Str());
      this->importMeshTexture(filePath);
      pEngineMesh->setTexture(m_pTextureMap[filePath]);
   }

   //MATERIAL
   aiColor3D diffuse;
   aiColor3D specular;
   aiColor3D ambient;

   pAiMaterial->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse);
   pAiMaterial->Get(AI_MATKEY_COLOR_SPECULAR, specular);
   pAiMaterial->Get(AI_MATKEY_COLOR_AMBIENT, ambient);

   pEngineMesh->setMaterialDiffuse(diffuse.r, diffuse.g, diffuse.b);
   pEngineMesh->setMaterialSpecular(specular.r, specular.g, specular.b);
   pEngineMesh->setMaterialAmbient(ambient.r, ambient.g, ambient.b);

   //forgive me
   for (int i = 0; i < pEngineMesh->getNumVertices(); ++i)
   {
      pEngineMesh->setVertexColor(i, diffuse.r, diffuse.g, diffuse.b);
   }

}
/*-----------------------------------------------------------------------------------------------*/
bool Importer::importMeshTexture( const std::string &texturePath )
{
   Texture::TexturePtr pTexture;

   std::map<std::string, Texture::TexturePtr>::iterator it;

   //look for the texture in the texture map
   for ( it = m_pTextureMap.begin(); it != m_pTextureMap.end(); ++it )
   {
      std::string mapFilePath = it->first.c_str();
      if( mapFilePath == texturePath )
      {
         pTexture = it->second;
         return true;
      }
   }

   ColorKey colorKey;

   colorKey.red   = 255;
   colorKey.green = 0;
   colorKey.blue  = 255;

   pTexture = Texture::TexturePtr( new Texture(texturePath, colorKey) );
   
   m_pTextureMap[texturePath] = pTexture;

   

   if ( !m_pRenderer->loadTexture(texturePath.c_str(), pTexture) )
   {
      std::cout << "Filed to import texture: " << texturePath << std::endl;
   }

   return true;
}
/*-----------------------------------------------------------------------------------------------*/
bool Importer::recursiveLoad( const aiScene* pAiScene, aiNode* pAiNode, Node* pResultNode )
{
   D3DXMATRIX nodeMatrix = convertToDxMatrix(pAiNode->mTransformation);
   pResultNode->setTransform(nodeMatrix);

   for (int i = 0; i < pAiNode->mNumMeshes; ++i)
   {
      unsigned int meshIndex = pAiNode->mMeshes[i];
      Mesh* currentMesh = convertToEngineMesh(pAiScene->mMeshes[meshIndex], pAiScene);

      pResultNode->addMesh(currentMesh);
   }

   for (int i = 0; i < pAiNode->mNumChildren; ++i)
   {
      Node* pCurrentNode = new Node(pAiNode->mChildren[i]->mName.C_Str());
      pResultNode->addChild(pCurrentNode);

      recursiveLoad(pAiScene, pAiNode->mChildren[i], pCurrentNode);
   }
   
   return true;
}
/*-----------------------------------------------------------------------------------------------*/
D3DXMATRIX Importer::convertToDxMatrix( aiMatrix4x4 aiMatrix )
{
   aiMatrix = aiMatrix.Transpose();

   D3DXMATRIX dxMatrix;

   dxMatrix._11 = aiMatrix.a1;
   dxMatrix._12 = aiMatrix.a2;
   dxMatrix._13 = aiMatrix.a3;
   dxMatrix._14 = aiMatrix.a4;

   dxMatrix._21 = aiMatrix.b1;
   dxMatrix._22 = aiMatrix.b2;
   dxMatrix._23 = aiMatrix.b3;
   dxMatrix._24 = aiMatrix.b4;
   
   dxMatrix._31 = aiMatrix.c1;
   dxMatrix._32 = aiMatrix.c2;
   dxMatrix._33 = aiMatrix.c3;
   dxMatrix._34 = aiMatrix.c4;
   
   dxMatrix._41 = aiMatrix.d1;
   dxMatrix._42 = aiMatrix.d2;
   dxMatrix._43 = aiMatrix.d3;
   dxMatrix._44 = aiMatrix.d4;

   return dxMatrix;
}
/*-----------------------------------------------------------------------------------------------*/
Mesh* Importer::convertToEngineMesh( aiMesh* pAiMesh , const aiScene* pAiScene)
{
   std::string name = pAiMesh->mName.C_Str();
   //////////////////////////////////////////////////////////////////////////
   //vertices
   int vertexCount = pAiMesh->mNumVertices;
   
   SuperVertex* vertices = new SuperVertex[vertexCount];
   
   for (int i = 0; i < vertexCount; ++i)
   {
      vertices[i].x = pAiMesh->mVertices[i].x;
      vertices[i].y = pAiMesh->mVertices[i].y;
      vertices[i].z = pAiMesh->mVertices[i].z;

      vertices[i].nx = pAiMesh->mNormals[i].x;
      vertices[i].ny = pAiMesh->mNormals[i].y;
      vertices[i].nz = pAiMesh->mNormals[i].z;

      if(pAiMesh->HasTextureCoords(0))
      {
         vertices[i].u = pAiMesh->mTextureCoords[0][i].x;
         vertices[i].v = pAiMesh->mTextureCoords[0][i].y;
      }

      vertices[i].color = 0xffffffff;
   }

   //////////////////////////////////////////////////////////////////////////
   // indices
   std::vector<unsigned int> aux;

   for (int i = 0; i < pAiMesh->mNumFaces; ++i)
   {
      aiFace face = pAiMesh->mFaces[i];

      for(int j = 0; j < face.mNumIndices; ++j)
      {
         aux.push_back(face.mIndices[j]);
      }
   }

   int indexCount = aux.size();

   unsigned int* indices = new unsigned int[indexCount];

   for(int i = 0; i < indexCount; ++i)
   {
      indices[i] = aux[i];
   }

   int primCount = pAiMesh->mNumFaces;
   
   
   //////////////////////////////////////////////////////////////////////////
   // Create Mesh
   Mesh* engineMesh = new Mesh(name, vertexCount, vertices, indexCount, indices, TRIANGLE_LIST, primCount);

   //////////////////////////////////////////////////////////////////////////
   //Materials
   importMeshMaterial(pAiMesh, pAiScene, engineMesh);

   return engineMesh;
}
/*-----------------------------------------------------------------------------------------------*/
std::string Importer::extractFileName( std::string filePath )
{
   if (filePath.empty())
   {
      std::cout << "Importer::extractFileName, empty filePath" << std::endl;
   }

   size_t lastSlash = filePath.find_last_of('/');
   std::string fileName = filePath.substr(lastSlash + 1);
   return fileName;
}
/*-----------------------------------------------------------------------------------------------*/
std::string Importer::extractFileLocation( std::string filePath )
{
   if (filePath.empty())
   {
      std::cout << "Importer::extractFileName, empty filePath" << std::endl;
   }

   size_t lastSlash = filePath.find_last_of('/');
   std::string fileLocation = filePath.substr(0, lastSlash + 1);
   return fileLocation;
}
/*-----------------------------------------------------------------------------------------------*/
Node* Importer::getModel( std::string name )
{ 
   if (name.empty())
   {
      std::cout << "Importer::getModel, name is empty" << std::endl; 
      return NULL;
   }
   
   name = extractFileName(name);

   return m_nodeMap[name]; 
}
